package com.hwtx.form.domain.handle;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.hwtx.form.annotation.Action;
import com.hwtx.form.annotation.FormType;
import com.hwtx.form.annotation.Log;
import com.hwtx.form.annotation.OperationType;
import com.hwtx.form.domain.def.FormDef;
import com.hwtx.form.domain.def.Select;
import com.hwtx.form.domain.handle.core.DefaultFormDataHandle;
import com.hwtx.form.domain.handle.core.DefaultFormDataReadHandle;
import com.hwtx.form.domain.listener.DynamicChangeFormHandleEvent;
import com.hwtx.form.domain.listener.EventPublisher;
import com.hwtx.form.domain.repo.FormRepo;
import com.hwtx.form.domain.vo.FormData;
import com.hwtx.form.domain.vo.FormRepData;
import com.hwtx.form.query.FormValueQuery;
import com.hwtx.form.util.BeanUtil;
import com.hwtx.form.util.FormUtil;
import io.geekidea.boot.framework.exception.BusinessException;
import jakarta.annotation.Resource;
import lombok.Builder;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator;
import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.event.EventListener;
import org.springframework.core.annotation.AnnotationUtils;

import java.util.*;

import static com.hwtx.form.domain.FormConstants.FORM_ID;
import static com.hwtx.form.util.BeanUtil.unRegistrySpringBean;
import static com.hwtx.form.util.FormUtil.getFromHandleClassByClassName;
import static com.hwtx.form.util.FormUtil.getFromHandles;

@FormType(value = "extConfig", comment = "扩展配置")
@Slf4j
public class ExtDataHandle implements FormDataHandle {

    @Resource
    private DefaultFormDataHandle defaultFormDataHandle;
    @Resource
    private DefaultFormDataReadHandle defaultFormDataReadHandle;
    @Resource
    private FormRepo formRepo;
    @Resource
    private ConfigurableApplicationContext context;
    @Resource
    private EventPublisher eventPublisher;

    @EventListener(ApplicationReadyEvent.class)
    public void init() {
        new Thread(() -> {
            Long formId = formRepo.getFormIdByName("ext");
            if (formId == null) {
                throw new BusinessException("无法获取扩展组件表单");
            }
            try {
                FormDef formDef = formRepo.getFormDef(formId);
                Map<String, Object> filters = Maps.newHashMap();
                filters.put("status", (byte) 1);
                List<Map<String, Object>> exts = defaultFormDataReadHandle.getCommonFormData(formDef, filters);
                List<String> successExt = Lists.newArrayList();
                exts.forEach(row -> {
                    String name = (String) row.get("name");
                    try {
                        if (loadExt((String) row.get("ext_content"), name, context)) {
                            log.info("加载扩展组件成功,name = {}", name);
                            successExt.add(name);
                        }
                    } catch (Exception e) {
                        log.info("加载扩展组件失败,name = {}", name, e);
                    }
                });
                initExt(successExt);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }).start();
    }

    private void initExt(List<String> extNames) {
        Map<String, FormDataHandle> exts = context.getBeansOfType(FormDataHandle.class);
        exts.forEach((name, handle) -> {
            if (extNames.contains(AopProxyUtils.ultimateTargetClass(handle).getName())) {
                handle.init();
            }
        });
    }

    @Action
    @Log(type = OperationType.GET, value = "货区扩展组件及action")
    public Select extAndActions(Map<String, Object> formData) {
        Select select = new Select();
        Long formId = formRepo.getFormIdByName("ext");
        if (formId == null) {
            throw new BusinessException("无法获取扩展组件表单");
        }
        FormDef formDef = formRepo.getFormDef(formId);
        if (formDef == null) {
            throw new BusinessException("无法获取扩展组件表单");
        }
        String parentId = FormUtil.getStrPostValue(formData, "parentId");
        Map<String, Object> filters = Maps.newHashMap();
        filters.put("status", (byte) 1);
        if (parentId == null) {
            select.setOptions(defaultFormDataReadHandle.getCommonFormData(formDef, filters).stream().map(row -> {
                Select.Option option = new Select.Option();
                String name = row.get("name").toString();
                Class<?> extComponent = getFromHandleClassByClassName(name);
                FormType formType = extComponent.getAnnotation(FormType.class);
                if (formType == null) {
                    return null;
                }
                option.setLabel(name);
                option.setValue(formType.value());
                return option;

            }).filter(Objects::nonNull).toList());
        } else {
            FormDataHandle formHandle = getFromHandles().get(parentId);
            if (formHandle == null) {
                return select;
            }
            select.setOptions(FormUtil.getHandleActionOptions(formHandle.getClass()));
        }
        return select;
    }

    @Action
    @Log(type = OperationType.UPDATE, value = "加载扩展组件")
    public boolean loadExt(Map<String, Object> formData) {
        beforeCheck(formData);
        if (formData.get("target") == null || StringUtils.isBlank(formData.get("target").toString())) {
            throw new BusinessException("目标内容不可为空");
        }
        try {
            ExtInfo extInfo = getExtInfo(formData);
            String content = extInfo.getContent();
            String extName = extInfo.getName();
            boolean ret = loadExt(content, extName, context);
            extInfo.getFormRepData().getData().put("status", extInfo.getStatus());
            defaultFormDataHandle.saveFormData(extInfo.getFormDef(), FormData.builder().data(extInfo.getFormRepData().getData()).build());
            initExt(Collections.singletonList(extName));
            return ret;
        } catch (Exception e) {
            log.error("加载扩展组件失败", e);
            throw new BusinessException("加载扩展组件失败");
        }
    }

    private boolean loadExt(String content, String name, ConfigurableApplicationContext context) throws Exception {
        Class<?> clazz = BeanUtil.compilerClass(content, name);
        if (clazz == null) {
            log.error("加载扩展组件失败, name = {}", name);
            return false;
        }
        FormType formType = clazz.getAnnotation(FormType.class);
        if (formType == null) {
            throw new BusinessException("扩展组件必须包含FormType注解");
        }
        String beanName = formType.value();
        if (context.containsBean(beanName)) {
            throw new BusinessException("扩展组件@FormType中名称重复");
        }
        if (context.containsBean(formType.value())) {
            return true;
        }
        String formTypeName = formType.value();
        if (Arrays.stream(clazz.getMethods()).anyMatch(method -> {
            Action annotation = AnnotationUtils.findAnnotation(method, Action.class);
            if (annotation != null) {
                String actionName = annotation.value();
                if (StringUtils.isBlank(actionName)) {
                    actionName = method.getName();
                }
                return Objects.equals(actionName, formTypeName);
            }
            return false;
        })) {
            throw new BusinessException("扩展组件不能有同名action,方法名 = " + formTypeName);
        }


        try {
            AnnotationAwareAspectJAutoProxyCreator annotationAwareAspectJAutoProxyCreator = context.getBean(AnnotationAwareAspectJAutoProxyCreator.class);
            annotationAwareAspectJAutoProxyCreator.setProxyClassLoader(clazz.getClassLoader());
            boolean ret = BeanUtil.registrySpringBean(clazz, beanName, context);
            Map<String, FormDataHandle> formHandles = Maps.newHashMap();
            FormDataHandle formDataHandle = (FormDataHandle) context.getBean(beanName);
            formHandles.put(FormUtil.getFromHandleType(formDataHandle).value(), formDataHandle);
            DynamicChangeFormHandleEvent event = new DynamicChangeFormHandleEvent(this, formHandles, true);
            eventPublisher.publishEvent(event);
            return ret;
        } catch (Exception e) {
            BeanUtil.unRegistrySpringBean(beanName, context);
        }
        return false;
    }

    @Action
    @Log(type = OperationType.UPDATE, value = "卸载扩展组件")
    public Object unLoadExt(Map<String, Object> formData) {
        beforeCheck(formData);
        Object ret;
        try {
            ExtInfo extInfo = getExtInfo(formData);
            String extName = extInfo.getName();
            extInfo.getFormRepData().getData().put("status", extInfo.getStatus());
            ret = defaultFormDataHandle.saveFormData(extInfo.getFormDef(), FormData.builder().data(extInfo.getFormRepData().getData()).build());
            FormDataHandle formDataHandle = context.getBeansOfType(FormDataHandle.class).values().stream().filter(handle -> handle.getClass().getName().startsWith(extName)).findFirst().orElse(null);
            if (formDataHandle != null) {
                String beanName = FormUtil.getFromHandleType(formDataHandle).value();
                unRegistrySpringBean(beanName, context);
                Map<String, FormDataHandle> formHandles = Maps.newHashMap();
                formHandles.put(beanName, formDataHandle);
                DynamicChangeFormHandleEvent event = new DynamicChangeFormHandleEvent(this, formHandles, false);
                eventPublisher.publishEvent(event);
                formDataHandle.destroy();
            }
        } catch (Exception e) {
            log.error("卸载扩展组件失败", e);
            throw new BusinessException("卸载扩展组件失败");
        }
        return ret;
    }

    private void beforeCheck(Map<String, Object> formData) {
        if (formData.get("id") == null || StringUtils.isBlank(formData.get("id").toString())) {
            throw new BusinessException("扩展组件id不能为空");
        }
        if (formData.get(FORM_ID) == null || StringUtils.isBlank(formData.get(FORM_ID).toString())) {
            throw new BusinessException("扩展组件表单id不能为空");
        }
        if (formData.get("name") == null || StringUtils.isBlank(formData.get("name").toString())) {
            throw new BusinessException("扩展组件参数name不能为空");
        }
        if (formData.get("status") == null || StringUtils.isBlank(formData.get("status").toString())) {
            throw new BusinessException("扩展组件参数status不能为空");
        }
    }

    private ExtInfo getExtInfo(Map<String, Object> formData) {
        FormDef formDef = formRepo.getFormDef(Long.valueOf(formData.get(FORM_ID).toString()));
        FormValueQuery query = new FormValueQuery();
        query.setValueIds(Collections.singletonList(Long.valueOf(formData.get("id").toString())));
        FormRepData.MapFormRepData formRepData = defaultFormDataHandle.getFormData(formDef, query);
        String content = null;
        if (formData.get("target") != null) {
            content = (String) formRepData.getData().get(formData.get("target").toString());
        }
        String name = (String) formRepData.getData().get(formData.get("name").toString());
        byte status = Byte.parseByte(formData.get("status").toString());
        return ExtInfo.builder().content(content).name(name).status(status).formRepData(formRepData).formDef(formDef).build();
    }

    @Builder
    @Getter
    static class ExtInfo {
        FormRepData.MapFormRepData formRepData;
        FormDef formDef;
        private String content;
        private String name;
        private byte status;
    }
}